package com.rpcfx.api.spi;

import com.rpcfx.api.RpcfxException;
import com.rpcfx.api.Serialization;
import io.protostuff.GraphIOUtil;
import io.protostuff.LinkedBuffer;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

/**
 * @ProjectName: learning
 * @Package: com.rpcfx.api.spi
 * @ClassName: ProtobufSerialization
 * @Description:
 * @Date: 2021/11/12 12:02 下午
 * @Version: 1.0
 */
public class ProtobufSerialization implements Serialization {
    @Override
    public byte[] serialize(Object object) {

        byte[] bytes;
        byte[] classNameBytes;
        LinkedBuffer buffer = LinkedBuffer.allocate();

        Schema schema = RuntimeSchema.getSchema(object.getClass());
        bytes = GraphIOUtil.toByteArray(object, schema, buffer);
        classNameBytes = object.getClass().getName().getBytes();

        int size = 4 + 4 + classNameBytes.length + bytes.length;
        try {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(size);
            DataOutputStream dos = new DataOutputStream(byteArrayOutputStream);

            dos.writeInt(classNameBytes.length);
            dos.writeInt(bytes.length);
            dos.write(classNameBytes);
            dos.write(bytes);
            buffer.clear();
            byte[] byteArray = byteArrayOutputStream.toByteArray();
            dos.close();
            byteArrayOutputStream.close();
            return byteArray;
        } catch (Exception e) {
            throw new RpcfxException(-1, e.getMessage());
        }
    }

    @Override
    public Object deserialize(byte[] input) {
        try {
            ByteArrayInputStream inputStream = new ByteArrayInputStream(input);
            DataInputStream dis = new DataInputStream(inputStream);
            int classNameLength = dis.readInt();
            int bytesLength = dis.readInt();

            if (classNameLength < 0 || bytesLength < 0) {
                throw new IOException();
            }

            byte[] classNameBytes = new byte[classNameLength];
            dis.readFully(classNameBytes, 0, classNameLength);

            byte[] bytes = new byte[bytesLength];
            dis.readFully(bytes, 0, bytesLength);

            String className = new String(classNameBytes);
            Class clazz = Class.forName(className);

            Object result;
            Schema schema = RuntimeSchema.getSchema(clazz);
            result = schema.newMessage();
            GraphIOUtil.mergeFrom(bytes, result, schema);
            return result;
        } catch (Exception e) {
            throw new RpcfxException(-1, e.getMessage());
        }
    }
}
